﻿using System;
using System.Runtime.InteropServices;
using Microsoft.SharePoint.Administration;
using System.Security.Cryptography.X509Certificates;
using System.Security;
using System.Security.Cryptography;
using System.IO;

namespace CodeCounsel.SharePoint2010.DigiD
{
    [Guid("34D973C3-BA39-4B81-8541-6CEA998B3F73")]
    public class DigiDService 
        : SPService
    {
        [Persisted]
        string _issuerName;
        [Persisted]
        byte[] _certificatePrivate;
        [Persisted]
        byte[] _certificatePublic;
        [Persisted]
        long _requestTimeout;

        public string IssuerName
        {
            get { return _issuerName; }
            set { _issuerName = value; }
        }

        public TimeSpan DigiDRequestTimeout
        {
            get { return new TimeSpan(_requestTimeout); }
            set 
            {
                if (value == null)
                {
                    throw new ArgumentNullException("Value");
                }
                _requestTimeout = value.Ticks;
            }
        }

        public X509Certificate2 SigningCertificate
        {
            get { return GetSigningCertificate(); }
        }

        public static DigiDService Local
        {
            get { return SPFarm.Local.Services.GetValue<DigiDService>(); }
        }

        public DigiDSettingsCollection DigiDSettings
        {
            get { return new DigiDSettingsCollection(this); }
        }

        #region Constructors
        public DigiDService()
        {
        }

        public DigiDService(SPFarm farm) 
            : base(String.Empty, farm)
        {

        }
	    #endregion

        public void ImportSigningCertificate(string filename, string password)
        {
            if (String.Equals(Path.GetExtension(filename), ".pfx", StringComparison.OrdinalIgnoreCase) == false)
            {
                throw new ArgumentOutOfRangeException("Only PFX certificates allowed");
            }
            X509Certificate2 certificate = new X509Certificate2(filename, password, 
                X509KeyStorageFlags.Exportable);
            ImportSigningCertificate(certificate);
        }

        public void ImportSigningCertificate(X509Certificate2 certificate)
        {
            if (certificate.HasPrivateKey == false)
            {
                throw new ArgumentOutOfRangeException("Certificate without private key");
            }
            RSACryptoServiceProvider privateKey = certificate.PrivateKey as RSACryptoServiceProvider;
            if (privateKey == null)
            {
                throw new ArgumentOutOfRangeException("Certificate without RSA private key");
            }
            if (privateKey.CspKeyContainerInfo.Exportable == false)
            {
                throw new ArgumentOutOfRangeException("Certificate without exportable RSA private key");
            }
            _certificatePublic = certificate.RawData;
            _certificatePrivate = privateKey.ExportCspBlob(true);
            Update();
        }

        public X509Certificate2 GetSigningCertificate()
        {
            try
            {
                X509Certificate2 certificate = new X509Certificate2(_certificatePublic);
                RSACryptoServiceProvider privateKey = new RSACryptoServiceProvider();
                privateKey.ImportCspBlob(_certificatePrivate);
                certificate.PrivateKey = privateKey;
                return certificate;
            }
            catch (CryptographicException e)
            {
                throw new DigiDException("Error retrieving signing certificate.", e);
            }
        }
    }
}
